package com.openatc.agent.service.arterial;

import algorithm.Kdalgorithm;
import com.openatc.agent.controller.DevController;
import com.openatc.agent.service.arterial.exception.IntersectionCycleException;
import com.openatc.agent.service.arterial.exception.KeyAgentIDException;
import com.openatc.model.model.Pattern;
import com.openatc.model.model.Split;
import com.openatc.model.model.StageForStageMode;
import com.openatc.optimize.fixedtimeplan.config.cross.PhaseOpt;
import com.openatc.optimize.fixedtimeplan.enums.OptTypeEnum;
import model.RouteIntsection;
import model.RouteOpt;
import model.RoutePara;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.util.List;
import java.util.logging.Logger;

/**
 * @author ：panleilei
 * @description 绿波协调策略
 * @modified By：
 * @version:
 * @date ：Created in 2024/1/23 13:38
 */
@Component
public class GreenWaveStrategy extends AbstractArterialTrafficControl {
    private static Logger logger = Logger.getLogger(GreenWaveStrategy.class.toString());

    private Kdalgorithm kdalgorithm = new Kdalgorithm();
    @Override
    public String strategy() {
        return ArterialTrafficControlStrategies.GREENWAVE.getStrategy();
    }

    @Override
    public RouteOpt optimize(RoutePara routePara) {
        //计算优化
        double intslenth[] = new double[routePara.getDevs().size()]; //保存路口长度的值
        double intslenthup[] = new double[routePara.getDevs().size()]; //保存上行路口长度的值
        double intslenthdown[] = new double[routePara.getDevs().size()]; //保存下行路口长度的值

        int intssplit[] = new int[routePara.getDevs().size()]; //保存路口协调相位绿信比的值

        int intsoffset[]; //保存优化后各路口的相位差的值
        int intsabs[] = new int[routePara.getDevs().size()];//各路口协调相位所属周期时间位置，用来计算绝对相位差
        int intscycle[] = new int[routePara.getDevs().size()];  // 保存各路口周期长度
        double intsvelup;
        double intsveldown;

        intsvelup = routePara.getUpspeed();
        intsveldown = routePara.getDownspeed();

        List<RouteIntsection> deviceList = routePara.getDevs();

        String keyAgentid = routePara.getKeyintsid();
        // 得到关键路口的周期
        int keyCycle = 0;
        try{
            keyCycle = calKeyIntersectionCycle(keyAgentid, deviceList);
        }catch (Exception e){
            throw new KeyAgentIDException(keyAgentid);
        }
        // 得到关键路口的相位差
        int keyoffset = calKeyIntersectionOffset(keyAgentid, deviceList);

        if (keyCycle == 0)
            throw new IntersectionCycleException(keyAgentid);

        // 优化各路口方案
//        int phaseno = 0;
        int lastIntsWidth = 0;
        for (RouteIntsection device : deviceList) {
            String fixedtimeplantype = device.getFixedtimeplantype();
            if (!StringUtils.hasLength(fixedtimeplantype)) {
                optType = OptTypeEnum.CYCLE_OPT;
            } else {
                optType = OptTypeEnum.getOptTypeEnumByValue(fixedtimeplantype);
            }
            List<PhaseOpt> phases = device.getPhases();
            Pattern pattern = device.getFeature().getPatternList().get(0);

            // 优化各路口周期
            try{
                cycleopt(keyCycle, device.getFeature(),phases);
            }catch (Exception e){
                logger.warning("Cycleopt Error: " + e.getMessage() + " Dev:" + device);
            }
            // 保存各路口周期
            intscycle[device.getSortid() - 1] = pattern.getCycle();
            // 距离默认为路口中心点之间的距离；如果设置了路口宽度，则距离应该是路口之间的路段距离
            //得到默认距离
            intslenth[device.getSortid() - 1] = device.getDistance();
            // 得到上行距离
            intslenthup[device.getSortid() - 1] = device.getDistance() + lastIntsWidth;
            // 得到下行距离
            intslenthdown[device.getSortid() - 1] = device.getDistance() + device.getWidth();

            // 保存上一个路口的宽度，用于计算上行时下个路口的距离
            lastIntsWidth = device.getWidth();

            //得到周期
            int cycle = pattern.getCycle();
            if (cycle == 0)
                throw new IntersectionCycleException(device.getAgentid());

            int phaseno;
            // 阶段模式取协调相位对应的阶段号
            if( "stage".equals( pattern.getContrloType() ) ){
                //根据相位号得到协调相位值
                if (routePara.getDirection().equals("up"))
                    phaseno = kdalgorithm.getStageNumFromPhase(pattern,device.getForwardphaseid() );
                else if (routePara.getDirection().equals("down"))
                    phaseno = kdalgorithm.getStageNumFromPhase(pattern,device.getBackphaseid() );
                else
                    phaseno = kdalgorithm.getStageNumFromPhase(pattern,device.getForwardphaseid() );
            }else{
                //根据相位号得到协调相位值
                if (routePara.getDirection().equals("up"))
                    phaseno = device.getForwardphaseid();
                else if (routePara.getDirection().equals("down"))
                    phaseno = device.getBackphaseid();
                else
                    phaseno = device.getForwardphaseid();
            }

            List<List<Split>> rings = pattern.getRings();

            label:
            for (int i = 0; i < rings.size(); i++) {
                List<Split> ring = rings.get(i);
                for (Split split : ring) {
                    if (split.getId() == phaseno) {
                        intssplit[device.getSortid() - 1] = split.getValue();
                        break label;
                    } else {
                        intsabs[device.getSortid() - 1] += split.getValue();
                    }
                }
            }

            while (intsabs[device.getSortid() - 1] >= cycle) {
                intsabs[device.getSortid() - 1] -= cycle;
            }
        }

        boolean allCycleEqual = isAllCycleEqual(intscycle);
        if (!allCycleEqual)
            // todo 填入有问题的路口id
            throw new IntersectionCycleException(keyAgentid);
        kdalgorithm.setAftcycle(keyCycle);
        if (routePara.getDirection().equals("up"))
            intsoffset = kdalgorithm.offsetOpt(routePara.getDirection(), intslenthup, intsvelup, intssplit);
        else if (routePara.getDirection().equals("down")) {
            intsoffset = kdalgorithm.offsetOpt(routePara.getDirection(), intslenthdown, intsveldown, intssplit);
            //调整相位差
            for (int i = 0; i < intssplit.length; i++) {
                if (intsoffset[i] != 0)
                    intsoffset[i] = keyCycle - intsoffset[i];
            }
        }
        else
            intsoffset = kdalgorithm.offsetByBiDirection(intslenth, intsvelup, intsveldown, intssplit);


        RouteOpt routeOpt = new RouteOpt();
        routeOpt.setDevs(routePara.getDevs());
        List<RouteIntsection> routeOptList = routeOpt.getDevs();

        //把每个路口相位差转成绝对相位差，start位置也会变化
        int keysoffset=0;
        for (RouteIntsection device : routeOptList) {
            int devNum = device.getSortid() - 1;
            intsoffset[devNum] -= intsabs[devNum];
            if (intsoffset[devNum] < 0)
                intsoffset[devNum] += keyCycle;
            if( device.getAgentid().equals(keyAgentid) )
                keysoffset = intsoffset[devNum];
        }

        // 按关键路口相位差统一对齐
        int moffset = keysoffset - keyoffset;// 关键路口相位差偏移量
        int soffsetNum = 0;
        for (int soffset : intsoffset) {
            intsoffset[soffsetNum] = soffset - moffset;
            if (intsoffset[soffsetNum] < 0)
                intsoffset[soffsetNum] += keyCycle;
            intsoffset[soffsetNum] = intsoffset[soffsetNum] % keyCycle;
            soffsetNum ++;
        }

        for (RouteIntsection device : routeOptList) {
            device.getFeature().getPatternList().get(0).setOffset(intsoffset[device.getSortid() - 1]);
        }

        // 计算绿波带宽
        routeOpt.setGreenwave(kdalgorithm.getBandWidth(null,routePara.getDirection(),intsvelup, intsveldown, routeOptList,intsoffset,keyAgentid ));
        return routeOpt;

    }


    /**
     * 判断周期长度是否一致
     * @param intscycle
     * @return
     */
    private boolean isAllCycleEqual(int[] intscycle) {
        if (intscycle.length == 0){
            return false;
        }
        int firstElement = intscycle[0];
        for (int i : intscycle){
            if (firstElement != i){
                return false;
            }
        }
        return true;
    }

}
